/**
 * @(#)VisitorWithContext.java, 2008-4-30.
 * Copyright 2008  Program Analysis Research Group(PARG),Institute of Software, Peking University.
 * All rights reserved.
 */
package edu.pku.sei.pgie.analyzer.internal;

import java.util.Stack;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;

/**
 * @author HeLi
 */
public class VisitorWithContext extends ASTVisitor {

    Stack<ASTNode> typeContext;

    Stack<ASTNode> methodContext;

    boolean enableAnoymousType;

    public VisitorWithContext(boolean enableAnonymousType) {
        this.enableAnoymousType = enableAnonymousType;
        typeContext = new Stack<ASTNode>();
        methodContext = new Stack<ASTNode>();
    }

    public void endVisit(MethodDeclaration node) {
        if (methodContext.size() > 0) {
            methodContext.pop();
        }
    }

    public void endVisit(TypeDeclaration node) {
        if (typeContext.size() > 0) {
            typeContext.pop();
        }
    }

    public boolean visit(MethodDeclaration node) {
        methodContext.push(node);
        return true;
    }

    public boolean visit(TypeDeclaration node) {
        typeContext.push(node);
        return true;
    }

    public void endVisit(AnonymousClassDeclaration node) {
        if (enableAnoymousType && typeContext.size() > 0) {
            typeContext.pop();
        }
    }

    public boolean visit(AnonymousClassDeclaration node) {
        if (enableAnoymousType) {
            typeContext.push(node);
            return true;
        }
        return false;
    }

    /**
     * Peek a type node from top of typeContext, and returns its Fully Qualified
     * Name
     *
     * @return if typeContext is empty or no ITypeBinding,return null
     */
    public String peekTypeFQN() {
        if (typeContext.size() < 1)
            return null;
        Object topType = typeContext.peek();
        ITypeBinding typeBinding = null;
        if (topType instanceof TypeDeclaration) {
            typeBinding = ((TypeDeclaration) topType).resolveBinding();
        } else if (topType instanceof AnonymousClassDeclaration) {
            typeBinding = ((AnonymousClassDeclaration) topType)
                    .resolveBinding();
            if (typeBinding == null)
                return null;
            typeBinding = typeBinding.getSuperclass();
            if (typeBinding == null)
                return null;
            if (typeBinding.getQualifiedName().compareTo("java.lang.Object") == 0) {
                ITypeBinding[] interfaces = typeBinding.getInterfaces();
                if (interfaces != null && interfaces.length > 0) {
                    typeBinding = interfaces[0];
                }
            }
        }
        if (typeBinding != null) {
            return typeBinding.getQualifiedName();
        }
        return null;
    }

    public String peekMethodFQN() {
        if (methodContext.size() < 1)
            return null;
        MethodDeclaration method = (MethodDeclaration) methodContext.peek();
        String typeFQN = peekTypeFQN();
        if (typeFQN != null) {
            return typeFQN + "." + method.getName().toString();
        }
        return null;
    }
}
